package com.xusan.spirng;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 注解ApplicationContext
 */
public class AnnotationApplicationContext {

    private Class cls;

    private ClassLoader classLoader;

    private String classLoaderStartPath;

    // 单例池
    private ConcurrentHashMap<String,Object> singletonObjects = new ConcurrentHashMap();

    // beanDefinationMap
    private ConcurrentHashMap<String,BeanDefination> beanDefinationMap = new ConcurrentHashMap();

    public AnnotationApplicationContext(Class cls) {
        this.cls = cls;

        // 解析配置类--->包扫描--->Component注解--->beanDefinationMap--->初始化
        // 判断是否包含Component注解
        if (cls.isAnnotationPresent(ComponentScan.class)){
            ComponentScan annotation = (ComponentScan) cls.getDeclaredAnnotation(ComponentScan.class);
            String path = annotation.value();
            System.out.println("包含ComponentSacn注解:"+path);
            // 根据扫描路径进行扫描,首先获取应用类加载器--->App ClassLoader
            classLoader = AnnotationApplicationContext.class.getClassLoader();
            // path转换/路径
            String resourcePath = path.replace(".", "/");
            classLoaderStartPath = path.substring(0, path.indexOf("."));
            URL resource = classLoader.getResource(resourcePath);
            // resource是一个目录
            File file = new File(resource.getFile());
            // 判断是否是目录
            if (file.isDirectory()){
                File[] fileList = file.listFiles();
                for (File f : fileList) {
                    // 通过类加载器创建bean到spring容器
                    loadClassRegisterContext(f);
                }
            }else {
                System.out.println("file不是目录");
            }

        }else {
            System.out.println("未包含ComponentSacn注解");
        }

        // 实例化单例池的bean
        for (Map.Entry<String,BeanDefination> entry: beanDefinationMap.entrySet()){
            BeanDefination beanDefination = entry.getValue();
            String beanName = entry.getKey();
            Object bean = creatBean(beanDefination);
            if (beanDefination.getScope().equals("singleton")) {
                singletonObjects.put(beanName,bean);
            }
        }

    }

    public Object creatBean(BeanDefination beanDefination) {
        try {
            Object o = beanDefination.getCls().getConstructor().newInstance();
            return o;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 首先判断是否存在beanDefinationMap，不存在抛出异常，存在判断是否单例，单例从单例池取
     * @param beanName
     * @return
     */
    public Object getBean(String beanName){
        if (beanDefinationMap.containsKey(beanName)) {
            BeanDefination beanDefination = beanDefinationMap.get(beanName);
            Object o = null;
            try {
                if (beanDefination.getScope().equals("singleton")){
                    o = singletonObjects.get(beanName);
                }else {
                    o = beanDefination.getCls().newInstance();
                }

            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            return o;
        }
        return null;
    }

    /**
     * 通过类加载器创建bean到spring容器
     * @param file
     */
    void loadClassRegisterContext(File file){
        if (file.isDirectory()){
            for (File listFile : file.listFiles()) {
                loadClassRegisterContext(listFile);
            }
        }else {
            // 根据File name通过类加载器加载为Class
            String absolutePath = file.getAbsolutePath();
            // 获取classLoaderPath
            String classLoaderPath = absolutePath.substring(absolutePath.indexOf(classLoaderStartPath), absolutePath.indexOf(".class"));
            String replacePath  = classLoaderPath.replace("\\", ".");

            Class<?> aClass = null;
            try {
                aClass = classLoader.loadClass(replacePath);
                // 判断aClass是否包含Component注解
                if (aClass.isAnnotationPresent(Component.class)){
                    System.out.println(aClass.getName()+"包含Component注解");
                    Component component = aClass.getDeclaredAnnotation(Component.class);
                    // 获取beanName
                    String beanName = component.name();
                    // 判断beanName是否存在
                    if (beanName.equals("") || beanName ==null) {
                        String aClassAbstractName = aClass.getName();
                        String[] split = aClassAbstractName.split("\\.");
                        String name = split[split.length-1];
                        String charName = name.substring(0, 1);
                        String lowCharName = charName.toLowerCase();
                        beanName = lowCharName+name.substring(1);
                    }
                    BeanDefination beanDefination = new BeanDefination();
                    // beanDefition添加beanClass
                    beanDefination.setCls(aClass);
                    // 判断作用域是否单例
                    if (aClass.isAnnotationPresent(Scope.class)) {
                        // 判断是否单例
                        Scope scope = aClass.getDeclaredAnnotation(Scope.class);
                        String value = scope.value();
                        if (value!=null && !value.equals("")) {
                            beanDefination.setScope(value);
                        }else {
                            beanDefination.setScope("singleton");
                        }
                    } else {
                        // 设置beanDefintion
                        beanDefination.setScope("singleton");
                    }
                    // 存放map
                    beanDefinationMap.put(beanName,beanDefination);

                }else {

                    System.out.println(aClass.getName()+"未包含Component注解");
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

        }

    }
}
